xtask\tasks\fuzz/
init_from_template.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Init new `fuzz/` directories using a preset template.
5
6use super::cargo_package_metadata;
7use anyhow::Context;
8
9#[derive(Clone, clap::ValueEnum)]
10pub enum Template {
11    /// Quick and easy `ChipsetDevice` fuzzing
12    ChipsetDevice,
13    /// Basic `cargo-fuzz` template
14    Basic,
15}
16
17pub(super) fn init_from_template(
18    ctx: &crate::XtaskCtx,
19    package: String,
20    template: Template,
21) -> Result<(), anyhow::Error> {
22    let selected_crate = 'selected_crate: {
23        let cargo_tomls = ignore::Walk::new(&ctx.root).filter_map(|entry| match entry {
24            Ok(entry) if entry.file_name() == "Cargo.toml" => Some(entry.into_path()),
25            Err(err) => {
26                log::error!("error when walking over subdirectories: {}", err);
27                None
28            }
29            _ => None,
30        });
31
32        for cargo_toml_path in cargo_tomls {
33            let manifest = cargo_toml::Manifest::<cargo_package_metadata::PackageMetadata>::from_path_with_metadata(
34                &cargo_toml_path,
35            )?;
36
37            if manifest
38                .package
39                .as_ref()
40                .map(|x| x.name())
41                .unwrap_or_default()
42                == package
43            {
44                break 'selected_crate cargo_toml_path;
45            }
46        }
47
48        anyhow::bail!("unknown crate '{}'", package)
49    };
50
51    let new_fuzz_target = format!("fuzz_{}", package);
52    let new_fuzz_dir = selected_crate.parent().unwrap().join("fuzz");
53
54    // make the new fuzz/ dir with the templated files
55    if new_fuzz_dir.exists() {
56        anyhow::bail!("{} already has a `fuzz/` folder!", package)
57    }
58    fs_err::create_dir(&new_fuzz_dir)?;
59    fs_err::write(
60        new_fuzz_dir.join("Cargo.toml"),
61        match template {
62            Template::Basic => include_str!("./templates/basic.template.toml"),
63            Template::ChipsetDevice => include_str!("./templates/chipset_device.template.toml"),
64        }
65        .to_string()
66        .replacen("$FUZZ_CRATE_NAME$", &new_fuzz_target, usize::MAX)
67        .replacen("$PARENT$", &package, usize::MAX)
68        .replacen("$FUZZ_TARGET_NAME$", &new_fuzz_target, usize::MAX),
69    )?;
70    fs_err::write(
71        new_fuzz_dir.join(format!("{}.rs", new_fuzz_target)),
72        match template {
73            Template::Basic => include_str!("./templates/basic.template.rs"),
74            Template::ChipsetDevice => include_str!("./templates/chipset_device.template.rs"),
75        },
76    )?;
77
78    // also update the root workspace toml
79    let new_workspace_entry = new_fuzz_dir.strip_prefix(&ctx.root)?.display().to_string();
80    let root_toml_raw = fs_err::read_to_string(ctx.root.join("Cargo.toml"))?;
81    let mut root_toml = root_toml_raw
82        .parse::<toml_edit::DocumentMut>()
83        .context("invalid root workspace Cargo.toml")?;
84    let members = &mut root_toml["workspace"]["members"].as_array_mut().unwrap();
85    // TODO: slot the new fuzz crate into the workspace members array in *sorted order*
86    //       (as opposed to appending blindly to the end of the array, as we do today)
87    members.push_formatted(toml_edit::Value::from(new_workspace_entry).decorated("\n  ", ""));
88    fs_err::write(ctx.root.join("Cargo.toml"), root_toml.to_string())?;
89
90    Ok(())
91}